package com.waitingsnow.common.share.bridia.net.oauth
{
    import com.waitingsnow.common.share.bridia.utils.BDStringUtils;
    import com.hurlant.crypto.Crypto;
    import com.hurlant.util.Base64;
    import com.waitingsnow.common.share.qqMicroBlog.support.WeiboUtil;
    import com.waitingsnow.common.share.util.DataUtils;
    
    import flash.net.URLRequestMethod;
    import flash.utils.ByteArray;
    
    /**
     *  
     * @author <a href="http://www.bridia.com">Bridia.com</a> : Tensharp
     * 
     * <p><b>示例地址： <a href="http://oauth.bridia.com">http://oauth.bridia.com</a></b></p>
     * 
     * <p><b>该版本只支持OAuth 1.0</b></p>
     * 
     * <p>OAuth详细说明文档请参考:<a href="http://oauth.net" target="_blank">OAuth.net</a></p>
     * 
     * <p>1.0 OAuth服务器端验证原理以及其他说明(Base string和signature原理请参考OAuth.net) : </p>
     * 
     * <ul>
     *	 <li>OAuth服务器端会验证用户提交的参数是否合法.如参数不合法会对非法(或缺失)参数进行报错.如参数合法OAuth服务器端会通过用户提交的所有参数重新生成一个签名来对比用户提交的oauth_signature是否正确.</li>
     *	 <li>服务提供商提供的接口在标注为需要登陆才可以使用的时候每请求一次该接口就需要生成一次签名.同时该接口需要使用的参数也要参与签名.</li>
     * </ul>
     * 
     * <p>2.0 <b>OAuth基本认证流程(详细示例请参考BDOAuthConnection.as) : </b></p>
     * 
     * <ul>
     * 	<li>Request token : 获取未授权的Request Token</li>
     * 	<li>Authorize : 请求用户授权Request Token (通常有2种方式,包括弹出授权页和弹出验证码.)</li>
     * 	<li>Access token : 通过Request Token换取Access Token</li>
     * </ul>
     * 
     * <p>3.0 请求参数说明 : </p>
     * 
     * <p>3.1 需要参与签名</p>
     * 
     * <ul>
     * 	<li>HTTP请求方式(例如GET,POST),必选.</li>
     * 	<li>请求的URL,必选.</li>
     * 	<li>Consumer secret,必选.申请应用时由服务提供商提供,需要参与签名.</li>
     * 	<li>代理地址,可选.在开发web应用时有可能需要使用</li>
     * </ul>
     * 
     * <p>3.2 Request token : </p>
     * 
     * <ul>
     * 	<li>oauth_consumer_key : 必选,申请应用时由服务提供商提供.</li>
     * 	<li>oauth_nonce : 必选.16-32位随机字符.</li>
     * 	<li>oauth_timestamp : 必选.时间戳,要保证用户提交的时间和OAuth服务器端的时间偏差小于5分钟.</li>
     * 	<li>oauth_signature_method : 必选.OAuth的签名方式,目前只支持HMAC-SHA1</li>
     * 	<li>oauth_version : 可选.OAuth的版本.</li>
     * 	<li>oauth_callback : 可选.只支持web应用.</li>
     * </ul>
     * 
     * <p>3.3 Authorize : </p>
     * 
     * <ul>
     * 	<li>其他参数同3.2,另外请求用户授权时请参考服务商提供的文档进行参数设置.</li>
     * 	<li>oauth_token : 必选,需要参与签名,同时需要添加到请求参数中.</li>
     * 	<li>oauth_token_secret : 必选,需要参与签名,但是不需要添加到请求参数中.</li>
     * </ul>
     * 
     * <p>3.4 Access token</p>
     * 
     * <ul>
     * 	<li>参数参考3.1和3.2</li>
     * 	<li>oauth_callback,web应用使用,可选,选择弹出授权页时可以使用.</li>
     * 	<li>oauth_verifier,桌面应用使用,必选,用户授权时选择弹出验证码方式必须要输入oauth_verifier.</li>
     * </ul>
     * 
     * <p>4.0 <b>使用BDOAuth类需要注意的地方 : </b></p>
     * 
     * <ul>
     * 	<li>oauth_timestamp和oauth_nonce为只读属性,base string时会自动生成,所以使用BDOAuth的时候要先执行baseString方法获取OAuth base string,然后才可以获取到合法的oauth_nonce和oauth_timestamp.</li>
     * 	<li>oauth_signature_method只支持HMAC-SHA1</li>
     * 	<li>在使用URL验证时需要对oauth_signature值进行URL编码,不推荐使用URL验证.</li>
     * </ul>
     */	
    
    public class BDOAuth
    {
        ///
        ///	[OAuth Version]
        ///
        
        /**
         * <label>支持的版本,目前只支持1.0.</label> 
         */		
        
        public function get version ():String
        {
            return "1.0";
        }
        
        ///
        /// [OAuth method]
        ///
        
        /**
         * @private 
         */
        
        private var _method:String = URLRequestMethod.GET;
        
        /**
         * <P>获取OAuth的请求方式.</P>
         * <p>method值请在获取base string的时候直接输入,默认为GET.</p>
         *  
         * @return 
         * 
         */		
        
        public function get method ():String
        {
            return _method.toUpperCase ();
        }
        
        ///
        ///	[OAuth Token]
        ///
        
        /**
         * @private 
         */		
        
        private var _oauth_token:String;
        
        /**
         * <p>OAuth token : Request token和Access Token后获取的oauth token</p>
         * <p>access token后获取的oauth_token可保存下来永久使用.</p>
         * 
         * @return 
         */
        
        public function get oauth_token ():String
        {
            return _oauth_token;
        }
        
        ///[setter]
        
        public function set oauth_token (value:String):void
        {
            _oauth_token = value;
        }
        
        ///
        ///	[OAuth token secret]
        ///
        
        /**
         * @private 
         */		
        
        private var _oauth_token_secret:String;
        
        /**
         * <p>OAuth secret : Request token和Access Token后获取的oauth secret</p>
         * <p>access token后获取的oauth_token_secret可保存下来永久使用.</p>
         * 
         * @return 
         */		
        
        public function get oauth_token_secret ():String
        {
            return _oauth_token_secret;
        }
        
        /// [setter]
        
        public function set oauth_token_secret (value:String):void
        {
            _oauth_token_secret = value;
        }
        
        ///
        ///	[OAuth timestamp]
        ///
        
        /**
         * @private 
         */		
        
        private var _oauth_timestamp:String;
        
        /**
         * 
         * <p>OAuth时间戳(每次获取Base string后会自动生成.).</p>
         *  
         * @return 
         * 
         */		
        
        public function get oauth_timestamp ():String
        {
            return _oauth_timestamp;
        }
        
        ///
        ///	[OAuth nonce]
        ///
        
        /**
         * @private 
         */		
        
        private var _oauth_nonce:String;
        
        /**
         * 
         * <p>OAuth nonce.单次值,随机字符串,防止重放攻击.(10-32位随机字符串),获取Base string后会自动生成.</p>
         *  
         * @return 
         * 
         */		
        
        public function get oauth_nonce ():String
        {
            return _oauth_nonce;
        }
        
        /**
         * 
         * <p>OAuth签名方式,目前只支持"HMAC-SHA1".</p>
         * 
         * @return 
         * 
         */
        
        public function get oauth_signature_method ():String
        {
            return "HMAC-SHA1";
        }
        
        ///
        ///	[OAuth consumer key]
        ///
        
        /**
         * @private 
         */		
        
        private var consumer_key:String;
        
        /**
         * 
         * <p>向服务商申请应用通过时由服务商提供 (ConsumerKey,参与签名.).</p>
         *  
         * @return 
         * 
         */		
        
        public function get oauth_consumer_key ():String
        {
            return consumer_key;
        }
        
        ///
        ///	[OAuth consumer secret]
        ///
        
        /**
         * @private 
         */		
        
        private var consumer_secret:String;
        
        /**
         * 
         * <p>向服务商申请应用通过时由服务商提供 (ConsumerSecret,参与签名.).</p>
         *  
         * @return 
         * 
         */		
        
        public function get oauth_consumer_secret ():String
        {
            return consumer_secret;
        }
        
        /**
         * @private
         * 
         * <p>oauth_nonce的长度.</p> 
         */		
        
        private var nonce_length:int;
        
        /**
         * @private
         * 
         * <p>用来存储每次添加的OAuth参数.</p> 
         */		
        
        private var parameters:Array = [];
        
        ///
        ///	[Constructor]
        ///
        
        /**
         * <p>创建一个新的OAuth对象.</p> 
         * 
         * @param consumerKey 向服务商申请应用通过时由服务商提供 (ConsumerKey,参与签名.).
         * @param consumerSecret 向服务商申请应用通过时由服务商提供 (ConsumerSecret,参与签名.).
         * @param nonceLength oauth_nonce的长度(建议长度位16-32位)
         * 
         */		
        
        public function BDOAuth (consumerKey:String, consumerSecret:String, nonceLength:int = 32)
        {
            consumer_key    = consumerKey;
            consumer_secret = consumerSecret;
            nonce_length    = nonceLength;
        }
        
        /// [Add ouath parameters]
        
        /**
         * <p>添加一个OAuth参数.需要注意的是参数里不能包括oauth_token,oauth_token_secret这两个参数.</p>
         * 
         * @param name 参数名.
         * @param value 参数值
         * @param encode 是否对参数进行编码.
         * 
         */		
        
        public function addOAuthParameters (name:String, value:String, encode:Boolean = false):void
        {
            parameters.push ([name, value, encode]);
        }
        
        ///
        ///	[Base string]
        ///
        
        /**
         * 
         * <p>获取OAuth Base string.</p>
         * 
         * <p>Base string的组成,详细参数说明请参考3.0或OAuth.net.</p>
         * 
         * <ul>
         * 	<li>HTTP请求方式</i>
         * 	<li>请求的URL</li>
         * 	<li>代理地址</li>
         * </ul>
         * <ul>
         * 	<li>oauth_consumer_key</li>
         * 	<li>oauth_nonce</li>
         * 	<li>oauth_timestamp</li>
         * 	<li>oauth_signature_method</li>
         * 	<li>oauth_version</li>
         * 	<li>其他oauth参数:oauth_callback, oauth_token, oauth_verifier</li>
         * 	<li>接口要求的必须参与签名的参数.</li>
         * </ul>
         *  
         * @param method HTTP请求方式
         * @param request_url 请求的URL,需要参与签名.
         * @param resource web应用(跨域)使用.
         * @return 
         * 
         */		
        
        public function baseString (method:String, request_url:String, resource:String = null):String
        {
            _oauth_nonce  = nonceMethod ();
            _oauth_timestamp  = stampMethod ();
            
            //
            
            var base:String     = "";
            var parameter:Array = [];
            
            //
            
            parameter.push (["oauth_consumer_key", consumer_key, false]);
            parameter.push (["oauth_nonce", _oauth_nonce, false]);
            parameter.push (["oauth_signature_method", oauth_signature_method, false]);
            parameter.push (["oauth_timestamp", _oauth_timestamp, false]);
            parameter.push (["oauth_version", "1.0", false]);
            
            //
            
            if (oauth_token && oauth_token.length > 0)
            {
                parameter.push (["oauth_token", oauth_token, false]);
            }
            
            //
            
            var i:int;
            var value:Array   = [];
            
            for (i = 0; i < parameters.length; i ++)
            {
                parameter.push ([parameters[i][0], parameters[i][1], parameters[i][2]]);
            }
            
            
            for (i = 0; i < parameter.length; i ++)
            {
                if (parameter[i][2] == false)
                {
                    value.push (parameter[i][0] + "=" + parameter[i][1]);
                }
                else
                {
                    value.push (parameter[i][0] + "=" + DataUtils.strEscape(parameter[i][1]));
                }
            }
            
            //
            
            value.sort ();
            
            base += BDStringUtils.special_url_encoder (method) + "&";
            base += BDStringUtils.special_url_encoder (request_url) + "&";
            base += escape (value.join ("&"));
            
            //
            
            parameters = [];
            
            //
            
            return base;
        }
        
        ///
        ///	[OAuth signature]
        ///
        
        /**
         *
         * <p>获取OAuth签名.</p>
         * 
         * <p>OAuth签名的组成,详细参数说明请参考3.0或OAuth.net.</p>
         * 
         * <ul>
         * 	<li>Consumer secret</li>
         * 	<li>oauth_token</li>
         * 	<li>base string</li>
         * </ul>
         *  
         * @param base OAuth Base string,详细请参考BDOAuth.baseString方法.
         * @return 
         * 
         */		
        
        public function signature (base:String):String
        {
            var secret:String = BDStringUtils.special_url_encoder (consumer_secret) + "&";
            
            //
            
            if (oauth_token_secret && oauth_token_secret.length > 0)
            {
                secret += oauth_token_secret;
            }
            
            //
            var text:ByteArray = new ByteArray();
            var k_secret:ByteArray = new ByteArray();
            
            text.writeUTFBytes(base);
            k_secret.writeUTFBytes(secret);
            
            return Base64.encodeByteArray(Crypto.getHMAC("hmac-sha1").compute(k_secret, text));
        }
        
        //
        
        /**
         * @private
         * 
         * <label>时间戳.</label>
         *  
         * @return 
         */		
        
        protected function stampMethod ():String
        {
            return (int (new Date ().getTime() / 1000)).toString ();
        }
        
        /**
         * @private
         * 
         * <label>OAuth nonce.</label>
         * 
         * @return 
         */
        
        protected function nonceMethod ():String
        {
            return WeiboUtil.getRandomChars(nonce_length);
        }
        
    }
}